#include "Game.h"
#include "Renderer.h"

#include "Beam.h"
#include "Projectile.h"
#include "Enemy.h"
#include "Tower.h"
#include "Follower.h"
#include "Player.h"
#include <assert.h>
#include "PacketObjectMove.h"
#include <iostream>
#include <SDL_events.h>
#include "ServerNetworkManager.h"
#include "ClientNetworkManager.h"
#include "PacketObjectSpawn.h"
#include "PacketObjectDestroy.h"
#include "PacketObjectMove.h"

#define Server(a) ((ServerNetworkManager*)a)
#define Client(a) ((ClientNetworkManager*)a)
static Player* player;
Game::Game(bool server)
	:
	m_server(server),
	m_networkManager(0),
	m_gameRunning(false),
	m_lastSpawn(0),
	m_killCount(0),
	m_fpsCounter(0),
	m_avgFps(0),
	m_fpsTimer(0)
	
{

}
Game::~Game(void)
{
	Renderer::GetInstance().Shutdown();
	//(*NetworkManager::GetInstance())->Shutdown();
	if (m_networkManager)
	{
		m_networkManager->Shutdown();
		delete m_networkManager;
	}
}
void Game::Start(void)
{
	if (m_server)
		m_networkManager = new ServerNetworkManager();
	else
		m_networkManager = new ClientNetworkManager();
		
	m_gameRunning = true;
	GameLoop();
}
void Game::Stop(void)
{
	m_gameRunning = false;
}
void Game::ForceStop(void)
{
	m_gameRunning = false;
	//this->~Game();
}

void Game::GameLoop(void)
{
	player = (Player*)m_objectManager.Add(Player(0, SCREEN_WIDTH / 2, SCREEN_HEIGHT - 80));
	while (m_gameRunning)
	{
		if (!HandleEvents())
			return;
		ProcessPackets();
		if (m_server)
		{
			if (Server(m_networkManager)->ClientConnected())
			{
				SpawnEnemies();
				UpdateObjects();
			}
		}
		else
		{

		}

		RenderAll();
		unsigned int ticksNow = SDL_GetTicks();
		if (ticksNow - m_fpsTimer >= 1000)
		{
			m_avgFps = m_fpsCounter;
			m_fpsTimer = ticksNow;
			m_fpsCounter = 0;
		}
		m_fpsCounter++;
		assert(strlen(SDL_GetError()) == 0);
	}
}
void Game::SpawnEnemies(void)
{
	if (SDL_GetTicks() > 20000)
		return;
	unsigned int ticksNow = SDL_GetTicks();

	if (ticksNow - m_lastSpawn > 50)
	{
		int x = rand() % SCREEN_WIDTH;
		Enemy e(x, 0);
		m_objectManager.Add(e);
		m_lastSpawn = ticksNow;
		SpawnObject(e);
	}
}
void Game::ProcessPackets(void)
{
	NetworkManager* mgr = m_networkManager;
	Packet* p = mgr->ReadPacket();
	while(p)
	{
		PACKET_TYPE type = p->GetType();
		if (type == PACKET_TYPE::OBJECT_SPAWN)
		{
			PacketObjectSpawn* packet = (PacketObjectSpawn*)p;
			OBJECT_TYPE objType = packet->GetObjectType();

			if (objType == OBJECT_TYPE::PLAYER)
			{
				Player player(packet->GetObjectID(), packet->GetPosition().x, packet->GetPosition().y);
				m_objectManager.Add(player);
			}
			else if (objType == OBJECT_TYPE::ENEMY)
			{
				Enemy enemy(packet->GetObjectID(), packet->GetPosition().x, packet->GetPosition().y);
				m_objectManager.Add(enemy);
			}
			else if(objType == OBJECT_TYPE::PROJECTILE)
			{
				Projectile projectile(packet->GetObjectID(), m_objectManager.FindObject(packet->GetOwnerID()), packet->GetPosition().x, packet->GetPosition().y);
				m_objectManager.Add(projectile);
			}
			else if (objType == OBJECT_TYPE::FOLLOWER)
			{
				Follower follower(packet->GetObjectID(), packet->GetPosition());
				m_objectManager.Add(follower);
			}


		}
		else if (type == PACKET_TYPE::OBJECT_DESTROY)
		{
			PacketObjectDestroy* packet = (PacketObjectDestroy*)p;
			m_objectManager.Remove(packet->GetObjectID());

		}
		else if (type == PACKET_TYPE::OBJECT_MOVE)
		{
			PacketObjectMove* packet = (PacketObjectMove*)p;
			GameObject* obj = m_objectManager.FindObject(packet->GetObjectID());
			if (obj)
				obj->SetPosition(packet->GetPosition().x, packet->GetPosition().y);
		}
		delete p;
		p = mgr->ReadPacket();
	}
}
bool Game::HandleEvents(void)
{
	SDL_Event event = {0};	
	while (SDL_PollEvent(&event))
	{
		if (event.type == SDL_KEYDOWN)
		{
			if (event.key.keysym.sym == SDLK_a)
			{
				player->SetKeyState(KEY_STATE::LEFT_PRESSED);
			}
			else if (event.key.keysym.sym == SDLK_d)
			{
				player->SetKeyState(KEY_STATE::RIGHT_PRESSED);
			}
			else if (event.key.keysym.sym == SDLK_SPACE)
			{
				Projectile projectile(player, player->GetPosition().x, player->GetPosition().y);
				projectile.GetPosition().x += (player->GetPosition().width / 2) - (projectile.GetPosition().width / 2);
				SpawnObject(projectile);
			}
			else if (event.key.keysym.sym == SDLK_x)
			{
				m_objectManager.PrintObjects();
			}
		}
		else if (event.type == SDL_KEYUP)
		{
			if (event.key.keysym.sym == SDLK_a || event.key.keysym.sym == SDLK_d)
			{
				player->SetKeyState(KEY_STATE::IDLE);
			}
		}
		else if (event.type == SDL_MOUSEBUTTONDOWN)
		{
			if (event.button.button == SDL_BUTTON_LEFT)
			{
				Follower follower(event.button.x, event.button.y);
				SpawnObject(follower);
			}
			else if (event.button.button == SDL_BUTTON_RIGHT)
			{
				ObjectNode* node = m_objectManager.GetFirstObject();
				while (node)
				{
					Follower* follower = dynamic_cast<Follower*>(node->obj);
					if (follower)
						follower->SetTarget(event.button.x, event.button.y);
					node = node->next;
				}
			}
		}
		else if (event.type == SDL_QUIT)
		{
			m_gameRunning = false;
			return false;
		}
	}
	return true;
}
void Game::UpdateObjects(void)
{
	ObjectNode* node = m_objectManager.GetFirstObject();
	GameObject* removeObject = 0;
	while(node)
	{
		node->obj->Update();
		if (node->obj->GetObjType() == OBJECT_TYPE::PROJECTILE)
		{
			if (node->obj->GetPosition().y < 0)
				removeObject = node->obj;
			else if (CheckCollisions((Projectile*)node->obj))
				removeObject = node->obj;
		}
		else if (node->obj->GetObjType() == OBJECT_TYPE::TOWER)
		{
			ObjectNode* nodeRange = m_objectManager.GetFirstObject();
			Tower* tower = (Tower*)node->obj;
			while (nodeRange)
			{
				float distance = tower->GetDistanceTo(*nodeRange->obj);
				if (distance <= tower->GetRange() && nodeRange->obj->GetObjType() == OBJECT_TYPE::ENEMY)
				{
					tower->Shoot(nodeRange->obj);
					m_objectManager.Add(Beam(tower, nodeRange->obj));
				}
				nodeRange = nodeRange->next;
			}
		}
		else if (node->obj->GetObjType() == OBJECT_TYPE::BEAM)
		{
			if (((Beam*)node->obj)->GetFramesRemaining() == 0)
			{
				removeObject = node->obj;
			}
		}
		else if (node->obj->GetObjType() == OBJECT_TYPE::ENEMY)
		{
			if (node->obj->GetPosition().y > SCREEN_HEIGHT)
			{
				removeObject = node->obj;
			}
			else if (((Enemy*)node->obj)->GetHealth() <= 0)
			{
				removeObject = node->obj;
				m_killCount++;
			}
		}

		if (m_server)
		{
			if (node->obj->GetObjType() == OBJECT_TYPE::PLAYER || node->obj->GetObjType() == OBJECT_TYPE::ENEMY || node->obj->GetObjType() == OBJECT_TYPE::PROJECTILE || node->obj->GetObjType() == OBJECT_TYPE::FOLLOWER)
			{
				PacketObjectMove packet(node->obj->GetID(), node->obj->GetPosition());
				Server(m_networkManager)->SendToClient(&packet);
			}
		}


		node = node->next;

		if (removeObject)
		{
			//removeObject->DestroyData();

			PacketObjectDestroy packet(removeObject->GetID());
			m_objectManager.Remove(removeObject->GetID());
			removeObject = 0;

			if (m_server)
				Server(m_networkManager)->SendToClient(&packet);
			/*else
				(GetClientNetworkManager())->SendToServer(&packet);*/
		}
	}
}
void Game::SpawnObject(GameObject& obj)
{
	m_objectManager.Add(obj);
	PacketObjectSpawn packet(obj.GetID(), obj.GetObjType(), obj.GetPosition());

	if (m_server)
		static_cast<ServerNetworkManager*>(m_networkManager)->SendToClient(&packet);
	else
		static_cast<ClientNetworkManager*>(m_networkManager)->SendToServer(&packet);
}
void Game::RenderAll(void)
{
	Renderer& renderer = Renderer::GetInstance();

	renderer.DrawBackground((SDL_Texture*)0);
	char buffer[50];
	sprintf(buffer, "Kills: %i", m_killCount);
	renderer.PrintText(buffer, 10, 10, 30);

	sprintf(buffer, "FPS: %i", m_avgFps);
	renderer.PrintText(buffer, SCREEN_WIDTH - 250, SCREEN_HEIGHT - 50, 30);

	sprintf(buffer, "BUFF_SIZE: %i", m_networkManager->GetBufferSize());
	renderer.PrintText(buffer, 150, 10, 30);

	sprintf(buffer, "OBJ_COUNT: %i", m_objectManager.GetCount());
	renderer.PrintText(buffer, 400, 10, 30);

	if (m_server && !(Server(m_networkManager)->ClientConnected()))
	{
		sprintf(buffer, "Waiting for client to connect...");
		renderer.PrintText(buffer, 10, SCREEN_HEIGHT / 2 -5, 40);
	}
	//renderer.Clear();
	ObjectNode* node = m_objectManager.GetFirstObject();
	while (node)
	{
		node->obj->Draw();
		node = node->next;
	}
	renderer.Render();
	renderer.Clear();
}

bool Game::CheckCollisions(Projectile* projectile)
{
	ObjectNode* node = m_objectManager.GetFirstObject();
		while (node)
		{
			if (projectile != node->obj && node->obj != projectile->GetOwner() && projectile->Collides(node->obj) && node->obj->GetObjType() != OBJECT_TYPE::PROJECTILE)
			{
				if (m_server)
				{
					PacketObjectDestroy packet(node->obj->GetID());
					Server(m_networkManager)->SendToClient(&packet);
				}

				m_objectManager.Remove(node->obj->GetID());
				m_killCount++;
				return true;
			}
			node = node->next;
		}
		return false;
}
